﻿using AutoMapper;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.Enum;
using IOA.MES.DataAccess.VModel;
using IOA.MES.Common;
using IOA.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IOA.MES.Log.Business;
using IOA.MES.Log.DataAccess.VModel;
using NLog;

namespace IOA.MES.Business
{
    /// <summary>
    /// 组件过站管理（错误代码：110001）
    /// </summary>
    public class ProductViaSiteMgr
    {
        private MESContext mesDb = new MESContext("MES");
        private Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 组件过站
        /// </summary>
        /// <param name="model">过站对象</param>
        /// <param name="images">过站图片列表</param>
        /// <param name="weldingData">焊机参数</param>
        /// <param name="topData">层压参数</param>
        /// <param name="ivData">IV数据</param>
        /// <param name="insulateData">绝缘耐压数据</param>
        /// <returns>过站对象</returns>
        public VProductViaSiteRecord ViaSite(VProductViaSiteRecord model, out int dutyId, List<VProductViaSiteImage> images = null,
            VProductViaSiteWeldData weldingData = null, VProductViaSiteTopData topData = null,
            VProductViaSiteIVData ivData = null,
            VProductViaSiteInsulateData insulateData = null)
        {
            var component = mesDb.Components.AsNoTracking().FirstOrDefault(_ => _.BarCode == model.Code && _.Valid);
            if (component != null)
            {
                if (!component.DistributeTime.HasValue)
                {
                    throw new MultiLanBizException(110002, model.Code);// "组件没有分片"
                }
                else if (component.ProductStatus == ProductStatus.Recycled)
                {
                    throw new MultiLanBizException(110023, model.Code);// "组件条码已被回收，无法过站"
                }
                else if (component.ProductStatus == ProductStatus.Scrapped && model.SiteType != SiteType.Packing)
                {
                    throw new MultiLanBizException(110024, model.Code);// "组件已被报废，无法过站"
                }
                else if (!string.IsNullOrWhiteSpace(component.PackNo))
                {
                    throw new MultiLanBizException(110029, $"{component.BarCode} / {component.PackNo}");//组件已被打包，无法过站，组件条码和包装编号分别为
                }
                else if (component.Reworking && !string.IsNullOrWhiteSpace(component.PackNo))
                {
                    throw new MultiLanBizException(110025, component.PackNo);//返工前必须先解包，包装编号：
                }
            }
            else if (component == null)
            {
                if (model.SiteType != SiteType.IVTest)
                {
                    throw new MultiLanBizException(110001, model.Code);// "组件条码不存在"
                }
                var standerdPiece = new StandardPieceMgr().GetStandardPiece(model.Code);
                if (standerdPiece == null)
                {
                    throw new MultiLanBizException(110001, model.Code);// "组件条码不存在"
                }
                else if (standerdPiece.StartDate > DateTime.Now)
                {
                    throw new MultiLanBizException(110018, model.Code);// "组件启用时间未到，请联系工艺"
                }
                else if (standerdPiece.EndDate.AddDays(1) < DateTime.Now)
                {
                    throw new MultiLanBizException(110019, model.Code);// "组件已过期，请联系工艺");
                }
                else
                {
                    var ivMachine = mesDb.Machines.AsNoTracking().FirstOrDefault(_ => _.PkId == model.MachineId && _.Valid);
                    if (ivMachine == null)
                    {
                        throw new MultiLanBizException(119003);// "设备不存在或已被删除"
                    }
                    mesDb.Machines.Attach(ivMachine);
                    ivMachine.ModifyBy = model.CreateBy;
                    ivMachine.ModifyTime = DateTime.Now;
                    if (ivData.Pmax < standerdPiece.Pmax - standerdPiece.PmaxToleranceDown || ivData.Pmax > standerdPiece.Pmax + standerdPiece.PmaxToleranceUp)
                    {
                        ivMachine.IvStandardPieceTestResult = false;
                        mesDb.SaveChanges();

                        var range = standerdPiece.PmaxToleranceDown == standerdPiece.PmaxToleranceUp ? $"±{standerdPiece.PmaxToleranceDown}" : $"-{standerdPiece.PmaxToleranceDown}+{standerdPiece.PmaxToleranceUp}";
                        throw new MultiLanBizException(110020, $"Pmax：{standerdPiece.Pmax}{range}，{ivData.Pmax}");
                    }
                    else if (ivData.Voc < standerdPiece.Voc - standerdPiece.VocToleranceDown || ivData.Voc > standerdPiece.Voc + standerdPiece.VocToleranceUp)
                    {
                        ivMachine.IvStandardPieceTestResult = false;
                        mesDb.SaveChanges();
                        var range = standerdPiece.VocToleranceDown == standerdPiece.VocToleranceUp ? $"±{standerdPiece.VocToleranceDown}" : $"-{standerdPiece.VocToleranceDown}+{standerdPiece.VocToleranceUp}";
                        throw new MultiLanBizException(110020, $"Voc：{standerdPiece.Voc}{range}，{ivData.Voc}");
                    }
                    else if (ivData.Isc < standerdPiece.Isc - standerdPiece.IscToleranceDown || ivData.Isc > standerdPiece.Isc + standerdPiece.IscToleranceUp)
                    {
                        ivMachine.IvStandardPieceTestResult = false;
                        mesDb.SaveChanges();
                        var range = standerdPiece.IscToleranceDown == standerdPiece.IscToleranceUp ? $"±{standerdPiece.IscToleranceDown}" : $"-{standerdPiece.IscToleranceDown}+{standerdPiece.IscToleranceUp}";
                        throw new MultiLanBizException(110020, $"Isc：{standerdPiece.Isc}{range}，{ivData.Isc}");
                    }
                    if (!StringUtils.Equals(ivMachine.Manufacturers, "众森"))
                    {
                        if (ivData.Vm < standerdPiece.Vmp - standerdPiece.VmpToleranceDown || ivData.Vm > standerdPiece.Vmp + standerdPiece.VmpToleranceUp)
                        {
                            ivMachine.IvStandardPieceTestResult = false;
                            mesDb.SaveChanges();
                            var range = standerdPiece.VmpToleranceDown == standerdPiece.VmpToleranceUp ? $"±{standerdPiece.VmpToleranceDown}" : $"-{standerdPiece.VmpToleranceDown}+{standerdPiece.VmpToleranceUp}";
                            throw new MultiLanBizException(110020, $"Vm：{standerdPiece.Vmp}{range}，{ivData.Vm}");
                        }
                        else if (ivData.Im < standerdPiece.Imp - standerdPiece.ImpToleranceDown || ivData.Im > standerdPiece.Imp + standerdPiece.ImpToleranceUp)
                        {
                            ivMachine.IvStandardPieceTestResult = false;
                            mesDb.SaveChanges();
                            var range = standerdPiece.ImpToleranceDown == standerdPiece.ImpToleranceUp ? $"±{standerdPiece.ImpToleranceDown}" : $"-{standerdPiece.ImpToleranceDown}+{standerdPiece.ImpToleranceUp}";
                            throw new MultiLanBizException(110020, $"Im：{standerdPiece.Imp}{range}，{ivData.Im}");
                        }
                        else if (ivData.FF < standerdPiece.FF - standerdPiece.FFToleranceDown || ivData.FF > standerdPiece.FF + standerdPiece.FFToleranceUp)
                        {
                            ivMachine.IvStandardPieceTestResult = false;
                            mesDb.SaveChanges();
                            var range = standerdPiece.FFToleranceDown == standerdPiece.FFToleranceUp ? $"±{standerdPiece.FFToleranceDown}" : $"-{standerdPiece.FFToleranceDown}+{standerdPiece.FFToleranceUp}";
                            throw new MultiLanBizException(110020, $"FF：{standerdPiece.FF}{range}，{ivData.FF}");
                        }
                    }
                    ivMachine.IvStandardPieceTestResult = true;
                    ivMachine.IvStandardPieceLastTime = DateTime.Now;
                    ivMachine.IvStandardPieceCode = standerdPiece.ProductCode;
                    mesDb.SaveChanges();
                    //标片测试通过
                    throw new MultiLanBizException(110021);
                }
            }
            var machine = new MachineMgr().GetMachine(model.MachineId);
            var workOrder = mesDb.WorkOrders.AsNoTracking().FirstOrDefault(_ => _.WorkOrderNo == component.WorkOrderNo && _.Valid);
            var lineIds = workOrder.LineId.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => Convert.ToInt32(_)).Where(_ => _ > 0).ToList();
            if (workOrder != null && machine != null && lineIds.Any() && !lineIds.Contains(machine.LineId))
            {
                throw new MultiLanBizException(110035, workOrder.LineName);//当前工单只能在如下产线过站：
            }

            var duty = new WorkshopDutyMgr().GetCurDuty(model.WorkshopCode);
            if (duty == null)
            {
                throw new MultiLanBizException(110027);//当前时间没有班次在工作
            }
            dutyId = duty.PkId;

            mesDb.Components.Attach(component);
            var @operator = new UserMgr().QueryUsers(new List<int> { model.CreateBy }).FirstOrDefault();
            var workshop = new WorkshopMgr().GetWorkshop(model.WorkshopCode);
            var config = new ViaSiteConfigMgr().GetConfig(component.OrderNo, component.WorkOrderNo);
            model.ViaSiteType = component.Testing ? ViaSiteType.OBA : (component.Reworking ? ViaSiteType.Rework : ViaSiteType.Produce);
            model.MachineName = machine?.MachineName;
            model.DutyId = duty.PkId;
            model.DutyName = duty.DutyName;
            model.WorkOrderNo = component.WorkOrderNo;
            model.OrderNo = component.OrderNo;
            model.CreateTime = DateTime.Now;
            model.WorkshopCode = workOrder.WorkshopCode;
            model.WorkshopName = workOrder.WorkshopName;
            model.LineId = machine.LineId;
            model.LineName = machine.LineName;

            #region 特殊站点过站条件判断
            List<VBinningPower> binningPowers = new List<VBinningPower>();
            VBinningPower power = null;
            VBinningCurrent current = null;
            //EL1、EL2必须上传图片
            if (new List<SiteType> { SiteType.EL1, SiteType.EL2 }.Contains(model.SiteType) && !(images ?? new List<VProductViaSiteImage>()).Any())
            {
                throw new MultiLanBizException(110026, model.Code);//请上传图片
            }
            //装框需判断拍照和接线盒条码
            if (model.SiteType == SiteType.Framing)
            {
                var frameConfig = config.ConfigModel.Framing;
                if (frameConfig.JunctionBoxBarCodeScan && string.IsNullOrWhiteSpace(model.BoxCode))
                {
                    throw new MultiLanBizException(110006, model.Code);// "请扫描接线盒条码");
                }
                if (frameConfig.JunctionBoxPhoto && (images == null || !images.Any()))
                {
                    throw new MultiLanBizException(110007, model.Code);// "请将接线盒拍照");
                }
            }
            else if (model.SiteType == SiteType.IVTest)
            {
                if (ivData == null)
                {
                    throw new MultiLanBizException(110008, model.Code);// "请上传IV点位数据");
                }
                else if (machine.IvStandardPieceTestResult == false)
                {
                    throw new MultiLanBizException(110022, model.Code);// "标片验证不通过，请重新验证标片"
                }

                if (machine.IvStandardPieceLastTime.HasValue) //最近一次测试过
                {
                    var workOrderStandPieces = new WorkOrderMgr().QueryWorkOrderStandardPieces(component.OrderNo, component.WorkOrderNo);
                    var lasIvPiece = workOrderStandPieces.FirstOrDefault(_ => string.Equals(_.ProductCode, machine.IvStandardPieceCode, StringComparison.CurrentCultureIgnoreCase));

                    if (!string.IsNullOrWhiteSpace(machine.IvStandardPieceCode) && lasIvPiece == null)//验过标片 && 标片不是当前工单的
                    {
                        throw new MultiLanBizException(110033, model.Code);//最近校验的标片与当前组件工单的标片不一致，无法过站
                    }

                    if (lasIvPiece != null && lasIvPiece.Frequency.HasValue //验证失效
                        && machine.IvStandardPieceLastTime.Value.AddHours((double)lasIvPiece.Frequency.Value) < DateTime.Now)
                    {
                        throw new MultiLanBizException(110034, $"{machine.IvStandardPieceLastTime.Value:HH:mm} / {lasIvPiece.Frequency.Value}小时");//标片校验有效时间已到，请重新校验标片，上次校验时间和校验频次为：
                    }
                }

                var ivTempConfig = new ViaSiteConfigMgr().GetIVTemp();
                if (ivTempConfig != null && ivData.TMod.HasValue
                   && (ivTempConfig.TempMin.HasValue && (ivTempConfig.TempMinContains && ivData.TMod < ivTempConfig.TempMin || !ivTempConfig.TempMinContains && ivData.TMod <= ivTempConfig.TempMin)
                       || ivTempConfig.TempMax.HasValue && (ivTempConfig.TempMaxContains && ivData.TMod > ivTempConfig.TempMax || !ivTempConfig.TempMaxContains && ivData.TMod >= ivTempConfig.TempMax)))
                {
                    var minTemp = ivTempConfig.TempMin.HasValue ? ivTempConfig.TempMin.ToString() : "不限";
                    var maxTemp = ivTempConfig.TempMax.HasValue ? ivTempConfig.TempMax.ToString() : "不限";
                    throw new MultiLanBizException(110030, $"{ivData.TMod}，{minTemp}-{maxTemp}");//温度不在允许范围内，当前温度和允许范围分别为
                }

                var ivConfig = config.ConfigModel.IVUpload;
                if (ivConfig.Graph && (images == null || !images.Any()))
                {
                    throw new MultiLanBizException(110009, model.Code);// "请上传IV曲线图");
                }
                binningPowers.AddRange(new BinningSchemeMgr().QueryWorkOrderBinningPowers(component.WorkOrderNo));
                power = binningPowers.FirstOrDefault(_ => ivData.Pmax >= _.PowerMin && ivData.Pmax < _.PowerMax);
                if (power == null)
                {
                    throw new MultiLanBizException(110011, $"{model.Code}：Pmax：{ivData.Pmax}");// "分档规则没有满足的功率档");
                }
                current = power.Currents.FirstOrDefault(_ => ivData.Im >= _.CurrentMin && ivData.Im < _.CurrentMax);
                if (current == null)
                {
                    throw new MultiLanBizException(110012, $"{model.Code}：Im：{ivData.Im}");// "分档规则没有满足的电流档");
                }
                var schema = new BinningSchemeMgr().GetBinningSchema(power.SchemaId);
                ivData.PowerId = power.PkId;
                ivData.IVPowerLevel = power.Power;
                ivData.IVCurrentLevel = current.LevelName;
                ivData.IVCurrentRange = $"{current.CurrentMin}-{current.CurrentMax}";
                ivData.SchemaName = schema?.SchemaName;
                ivData.NameplateModel = power.NameplateModel;

                model.PowerLevel = ivData.IVPowerLevel.Value;
                model.CurrentLevel = ivData.IVCurrentLevel;
                model.NameplateModel = ivData.NameplateModel;
            }
            else if (model.SiteType == SiteType.InsulatedResistance)
            {
                if (insulateData == null)
                {
                    throw new MultiLanBizException(110013, model.Code);// "请上传绝缘耐压数据");
                }
            }
            else if (model.SiteType == SiteType.FinalInspection && !model.IsLock)
            {
                model.QLevel = string.IsNullOrWhiteSpace(model.QLevel) ? "Q1" : model.QLevel;

                //if (!string.Equals(model.QLevel, "Q1", StringComparison.CurrentCultureIgnoreCase) && string.IsNullOrWhiteSpace(model.BadCode))
                //{
                //    throw new MultiLanBizException(110031, model.Code);//降级组件必须选择降级原因
                //}
            }
            #endregion

            #region 待复判组件
            if (model.SiteType == SiteType.FinalInspection && !model.IsLock)
            {
                var downgrade = mesDb.Downgrades.AsNoTracking().Where(_ => _.BarCode == model.Code && _.Valid).ToList()
                    .OrderByDescending(_ => _.PkId).FirstOrDefault();
                var finalinspectioned = mesDb.Components.FirstOrDefault(_ => _.BarCode == model.Code && _.Valid);
                //if (downgrade != null && !downgrade.Reviewed && !StringUtils.Equals(downgrade.QLevel, model.QLevel) && finalinspectioned.FinalInspectionTime==null)//增加非首次过站终检判断
                //{
                //    throw new MultiLanBizException(110017, model.Code);//终检等级与降级等级不一致，无法过站
                //}
                var redowngrade = new DowngradeMgr().GetDowngrade(model.BadCode);
                if (redowngrade != null && !redowngrade.Reviewed)
                {

                    redowngrade.ReviewLevel = model.QLevel;
                    redowngrade.ReviewedBy = model.CreateBy;

                    new DowngradeMgr().Review(redowngrade);
                }
            }
            #endregion

            #region 待解锁组件
            var lockRecord = mesDb.LockRecords.AsNoTracking().FirstOrDefault(_ => _.BarCode == model.Code && _.UnLockedBy <= 0 && _.Valid);
            if (lockRecord != null)
            {
                if (lockRecord.LockSite <= model.SiteType)
                {
                    throw new MultiLanBizException(110016, model.Code);//当前组件已锁定，无法过站：
                }
            }
            #endregion

            #region 匹配异常代码
            var productBadReasons = new List<ProductViaSiteBadReason>();
            if (new List<SiteType> { SiteType.NQC, SiteType.FinalInspection }.Contains(model.SiteType))
            {
                var total = 0;
                var badCodes = model.BadCode.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => _.ToUpper().Trim()).Distinct().ToList();
                var badReasons = new BadReasonMgr().QueryReasons(1, 0, out total, string.Empty, string.Empty).Where(_ => badCodes.Contains(_.ReasonCode, StringComparer.CurrentCultureIgnoreCase)).ToList();
                model.BadTypeCode = string.Join(",", badReasons.Select(_ => _.BadTypeCode));
                model.BadTypeName = string.Join(",", badReasons.Select(_ => _.BadTypeName));
                model.BadCode = string.Join(",", badReasons.Select(_ => _.ReasonCode));
                model.BadReason = string.Join(",", badReasons.Select(_ => _.ReasonCN));
                if (badReasons.Any(_ => new[] { "ZJ1", "ZJ2" }.Contains(_.ReasonCode, StringComparer.CurrentCultureIgnoreCase)))//清洗、打胶异常
                {
                    model.IsLock = true;
                }

                if (model.SiteType == SiteType.NQC)
                {
                    productBadReasons.AddRange(badReasons.Select(_ => new ProductViaSiteBadReason
                    {
                        JudgeType = JudgeType.NQC,
                        BarCode = model.Code,
                        BadTypeCode = _.BadTypeCode,
                        BadReasonCode = _.ReasonCode,
                        CreateBy = model.CreateBy,
                        CreateTime = DateTime.Now
                    }));
                }
            }
            #endregion

            #region 非检验和返工情况下：必须过站和重复过站判断
            var viaRecords = ConvertViaSiteRecords(Mapper.Map<VComponent>(component));
            model.ViaSiteType = model.ViaSiteType != ViaSiteType.Produce ? model.ViaSiteType
                : (viaRecords.Any(_ => _.SiteType == model.SiteType) ? ViaSiteType.ReViaSite : ViaSiteType.Produce);//判断是否为重复过站

            var userRoles = (@operator == null || model.SiteType != SiteType.NameplatePrinting) ? new List<UserRole>()
                : mesDb.UserRoles.AsNoTracking().Where(_ => _.UserId == @operator.PkId && _.Valid).ToList();
            if (userRoles.Any(_ => _.RoleId == 35))
            {
                //拥有铭牌补打权限（RoleId：35），跳过重复过站判断
            }
            else if (config != null && config.ConfigModel != null && component.ProductStatus != ProductStatus.Scrapped && !StringUtils.Equals("BF", component.QLevel))
            {
                if (config.ConfigModel.ViaSites != null && config.ConfigModel.ViaSites.Any(_ => _.SiteType == model.SiteType && model.SiteType != SiteType.Top) //存在过站配置，并且不是层压
                    && !component.Testing && !component.Reworking) //不是返工和OBA测试
                {
                    #region 重复过站判断
                    if (viaRecords.Any(_ => _.SiteType == model.SiteType))
                    {
                        //几个拍照和检测的站点，如果下一站还没过，重新拍照即可，不算重复过站
                        if (new List<SiteType> { SiteType.EL1, SiteType.NQC, SiteType.Framing, SiteType.EL2, SiteType.IVTest, SiteType.FinalInspection }.Contains(model.SiteType)
                            && !viaRecords.Any(_ => _.SiteType > model.SiteType))
                        {
                            var siteRecord = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.Code == component.BarCode && _.SiteType == model.SiteType && _.Valid)
                                .OrderByDescending(_ => _.PkId).FirstOrDefault();
                            if (siteRecord == null)
                            {
                                siteRecord = Mapper.Map<ProductViaSiteRecord>(model);
                                mesDb.ProductViaSiteRecords.Add(siteRecord);
                                mesDb.SaveChanges();
                            }
                            else
                            {
                                mesDb.ProductViaSiteRecords.Attach(siteRecord);
                                siteRecord.ModifyBy = model.CreateBy;
                                siteRecord.ModifyTime = DateTime.Now;

                                //删除以前的图片
                                var existImages = mesDb.ProductViaSiteImages.AsNoTracking().Where(_ => _.ViaSiteId == siteRecord.PkId && _.Valid).ToList();
                                existImages.ForEach(_ =>
                                {
                                    mesDb.ProductViaSiteImages.Attach(_);
                                    _.Valid = false;
                                    _.ModifyBy = model.CreateBy;
                                    _.ModifyTime = DateTime.Now;
                                });

                                //添加新图片
                                mesDb.ProductViaSiteImages.AddRange((images ?? new List<VProductViaSiteImage>()).Select(_ => new ProductViaSiteImage
                                {
                                    SiteType = _.SiteType,
                                    BarCode = _.BarCode,
                                    Image = _.Image,
                                    Positions = _.Positions,
                                    ViaSiteId = siteRecord.PkId,
                                    SortNo = images.IndexOf(_) + 1,
                                    CreateBy = model.CreateBy,
                                    CreateTime = DateTime.Now
                                }));
                            }

                            if (model.SiteType == SiteType.EL1)
                            {
                                siteRecord.QLevel = model.QLevel;

                                component.EL1Image = images?.FirstOrDefault()?.Image ?? string.Empty;
                                component.EL1OncePass = StringUtils.Equals(model.QLevel, "Q1");
                            }
                            else if (model.SiteType == SiteType.NQC)
                            {
                                siteRecord.QLevel = model.QLevel;
                                siteRecord.BadCoordinates = model.BadCoordinates;
                                siteRecord.BadCount = model.BadCount;

                                siteRecord.BadTypeCode = model.BadTypeCode;
                                siteRecord.BadTypeName = model.BadTypeName;
                                siteRecord.BadCode = model.BadCode;
                                siteRecord.BadReason = model.BadReason;
                                var oldReasons = mesDb.ProductViaSiteBadReasons.AsNoTracking().Where(_ => _.JudgeType == JudgeType.NQC && _.JudgeId == siteRecord.PkId && _.Valid).ToList();
                                oldReasons.ForEach(_ =>
                                {
                                    mesDb.ProductViaSiteBadReasons.Attach(_);
                                    _.Valid = false;
                                    _.ModifyBy = model.CreateBy;
                                    _.ModifyTime = DateTime.Now;
                                });
                                productBadReasons.ForEach(_ => _.JudgeId = siteRecord.PkId);
                                mesDb.ProductViaSiteBadReasons.AddRange(productBadReasons);

                                component.NQCOncePass = StringUtils.Equals(model.QLevel, "Q1");
                            }
                            else if (model.SiteType == SiteType.Framing)
                            {
                                component.BoxImage = images?.FirstOrDefault()?.Image ?? string.Empty;
                            }
                            else if (model.SiteType == SiteType.EL2)
                            {
                                siteRecord.QLevel = model.QLevel;

                                component.EL2Image = images?.FirstOrDefault()?.Image ?? string.Empty;
                                component.EL2OncePass = StringUtils.Equals(model.QLevel, "Q1");
                            }
                            else if (model.SiteType == SiteType.IVTest)
                            {
                                var existIVDatas = mesDb.ProductViaSiteIVDatas.AsNoTracking().Where(_ => _.ViaSiteId == siteRecord.PkId && _.Valid).ToList();
                                existIVDatas.ForEach(_ =>
                                {
                                    mesDb.ProductViaSiteIVDatas.Attach(_);
                                    _.Valid = false;
                                    _.ModifyBy = model.CreateBy;
                                    _.ModifyTime = DateTime.Now;
                                });
                                ivData.ViaSiteId = siteRecord.PkId;
                                var ivDataEntity = Mapper.Map<ProductViaSiteIVData>(ivData);
                                ivDataEntity.CreateTime = DateTime.Now;
                                mesDb.ProductViaSiteIVDatas.Add(ivDataEntity);

                                component.IvImage = images?.FirstOrDefault()?.Image ?? string.Empty;
                                component.NameplateModel = ivData.NameplateModel;
                                component.IVPowerLevel = power.Power;
                                component.IVCurrentLevel = current.LevelName;
                                component.ConversionEfficiency = component.TheoreticalPower == 0 ? 0 : ((ivData.Pmax ?? 0) / component.TheoreticalPower);

                                component.Isc = ivData.Isc;
                                component.Voc = ivData.Voc;
                                component.Vm = ivData.Vm;
                                component.Im = ivData.Im;
                                component.FF = ivData.FF;
                                component.Pmax = ivData.Pmax;
                                component.Temp = ivData.Temp;
                                component.Ress = ivData.Ress;
                                component.Ressh = ivData.Ressh;
                                component.ModEff = ivData.ModEff;
                                component.SunRef = ivData.SunRef;
                            }
                            else if (model.SiteType == SiteType.FinalInspection)
                            {
                                siteRecord.QLevel = model.QLevel;
                                siteRecord.IsLock = model.IsLock;
                                siteRecord.BadReason = model.BadReason;
                                siteRecord.ComponentType = model.ComponentType;
                                siteRecord.CellFeature = model.CellFeature;


                                component.QLevel = model.QLevel;
                                component.FinalInspectionCellFeature = model.CellFeature;
                                component.FinalInspectionComponentType = model.ComponentType;
                                component.FinalInspectionBadReason = model.BadReason;
                                component.FinalInspectionOncePass = StringUtils.Equals(model.QLevel, "Q1");
                                component.VerifyCode = model.VerifyCode;

                                if (!string.IsNullOrWhiteSpace(component.QLevel) && StringUtils.Equals(model.QLevel, "Q1") && !StringUtils.Equals(component.QLevel, "Q1"))
                                {
                                    throw new MultiLanBizException(110037);//组件已被降级，终检无法升级为Q1
                                }
                                if (model.IsLock)
                                {
                                    //组件在终检被锁定
                                    new LockMgr().Lock(new List<string> { model.Code }, SiteType.FinalInspection, SiteType.Packing, model.CreateBy, "终检锁定");
                                }
                                if (!StringUtils.Equals(model.QLevel, "Q1")
                                    && !StringUtils.Equals(model.QLevel, "Fanqingxi") && !StringUtils.Equals(model.QLevel, "bujiao") &&
                                    !StringUtils.Equals(model.QLevel, "ELBL") && !StringUtils.Equals(model.QLevel, "WGBL"))
                                {
                                    //组件在终检被降级  20230207  组件降级等级不为
                                    new DowngradeMgr().Downgrade(new List<string> { component.BarCode },
                                        model.BadCode.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(), SiteType.FinalInspection, model.QLevel, model.CreateBy);
                                }
                            }
                            component.ModifyBy = model.CreateBy;
                            component.ModifyTime = DateTime.Now;

                            mesDb.SaveChanges();
                            return Mapper.Map<VProductViaSiteRecord>(siteRecord);
                        }
                        //不允许重复过站
                        if (!config.ConfigModel.ViaSites.FirstOrDefault(_ => _.SiteType == model.SiteType).MultipleVia)
                        {
                            throw new MultiLanBizException(110003, model.Code);// "该站点已有过站记录，不允许重复过站"
                        }
                    }
                    #endregion

                    #region 前面一个必须过站判断
                    var lastSite = config.ConfigModel.ViaSites.OrderBy(_ => _.SiteType).Where(_ => _.SiteType < model.SiteType && _.ForceVia).LastOrDefault();
                    if (lastSite != null && !viaRecords.Any(_ => _.SiteType == lastSite.SiteType && _.SiteType != SiteType.LaminatorRework))
                    {
                        throw new MultiLanBizException(110014, model.Code + "：" + (!Enum.IsDefined(typeof(SiteType), lastSite.SiteType) ? string.Empty : EnumConvert.ToDesc(lastSite.SiteType)));// 下列站点尚未过站
                    }
                    #endregion
                }

                //装框后的站点固化时间判断
                var framingRecord = viaRecords.FirstOrDefault(_ => _.SiteType == SiteType.Framing);
                if (config.ConfigModel.QTime != null && config.ConfigModel.QTime.SolidifyTime.HasValue && config.ConfigModel.QTime.SolidifyTime.Value > 0 //判定存在固化时间配置
                    && model.SiteType > SiteType.Framing && !viaRecords.Any(_ => _.SiteType > SiteType.Framing && _.SiteType < model.SiteType) //判定之前没有做过固化判断
                    && framingRecord != null && (DateTime.Now - framingRecord.CreateTime).TotalMinutes < config.ConfigModel.QTime.SolidifyTime.Value) //判定固化时间不足
                {
                    throw new MultiLanBizException(110028, $"{model.Code}：{framingRecord.CreateTime:MM-dd HH:mm}，{(int)(DateTime.Now - framingRecord.CreateTime).TotalMinutes}/{config.ConfigModel.QTime.SolidifyTime.Value}");// "该站点已有过站记录，不允许重复过站"
                }
            }
            #endregion

            #region 分拣机分拣
            if (model.SiteType == SiteType.Sort)
            {
                new SortMgr().SortComponent(new VSortRecord
                {
                    BarCode = component.BarCode,
                    MachineId = machine.PkId,
                    CreateBy = model.CreateBy
                });
                //对于清洗不良、补胶直接设置为Q1合格组件
                if (component.QLevel == "bujiao" || component.QLevel == "Fanqingxi")
                {
                    component.QLevel = "Q1";
                }
                mesDb.SaveChanges();
            }
            #endregion

            #region BOM扣料
            var productSiteBoms = mesDb.ProductSiteBoms.AsNoTracking().Where(_ => _.WorkOrderNo == model.WorkOrderNo && _.SiteType == model.SiteType && _.Valid).ToList();
            if (productSiteBoms.Any())
            {
                var siteMaterials = new List<ProductViaSiteMaterial>();
                var workOrderBoms = mesDb.WorkOrderBoms.AsNoTracking().Where(_ => _.WorkOrderNo == component.WorkOrderNo && _.Valid).ToList();
                foreach (var siteBom in productSiteBoms)
                {
                    var storages = mesDb.LineStorages.AsNoTracking().Where(_ => _.LineId == model.LineId && _.WorkOrderNo == model.WorkOrderNo && _.CategoryName == siteBom.BomCategoryName && _.Quantity > 0 && _.Valid).ToList();//先找有这个站点的分类库存
                    if (!storages.Any())
                    {
                        throw new MultiLanBizException(110036, siteBom.BomCategoryName);//物料无库存
                    }

                    var storage = storages.OrderBy(_ => _.PkId).FirstOrDefault();//先进先出
                    var workOrderBom = workOrderBoms.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, storage.ProductCode) && StringUtils.Equals(_.ProductCompany.Trim(';'), storage.ProductCompany.Trim(';'))); //先找同厂家
                    workOrderBom = workOrderBom != null ? workOrderBom : workOrderBoms.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, storage.ProductCode));//再找同料号
                    if (workOrderBom == null)
                    {
                        //料号没找到，那就看有什么BOM料号
                        var curBomProducts = workOrderBoms.Where(_ => StringUtils.Equals(_.CategoryName, siteBom.BomCategoryName)).ToList();
                        if (!curBomProducts.Any())
                        {
                            throw new MultiLanBizException(110036, siteBom.BomCategoryName);//物料无库存
                        }

                        storage = storages.Where(_ => curBomProducts.Any(bom => StringUtils.Equals(_.ProductCode, bom.ProductCode) && StringUtils.Equals(_.ProductCompany.Trim(';'), bom.ProductCompany.Trim(';'))))
                            .OrderBy(_ => _.PkId).FirstOrDefault();//找到现有的库存
                        if (storage != null)
                        {
                            workOrderBom = curBomProducts.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, storage.ProductCode) && StringUtils.Equals(_.ProductCompany.Trim(';'), storage.ProductCompany.Trim(';')));
                        }
                        else
                        {
                            storage = storages.Where(_ => curBomProducts.Any(bom => StringUtils.Equals(_.ProductCode, bom.ProductCode))).OrderBy(_ => _.PkId).FirstOrDefault();//找到现有的库存
                            if (storage != null)
                            {
                                workOrderBom = curBomProducts.FirstOrDefault(_ => StringUtils.Equals(_.ProductCode, storage.ProductCode));
                            }
                            else
                            {
                                throw new MultiLanBizException(110036, siteBom.BomCategoryName);//物料无库存
                            }
                        }
                    }

                    var curSiteBom = new ProductViaSiteMaterial
                    {
                        OrderNo = model.OrderNo,
                        WorkOrderNo = model.WorkOrderNo,
                        BarCode = model.Code,
                        WorkshopCode = workshop.WorkshopCode,
                        WorkshopName = workshop.WorkshopName,
                        DutyId = duty.PkId,
                        DutyName = duty.DutyName,
                        SiteType = model.SiteType,
                        MachineId = machine.PkId,
                        MachineName = machine.MachineName,
                        MaterielCategoryCode = storage.CategoryCode,
                        MaterielCategoryName = storage.CategoryName,
                        MaterielCode = storage.ProductCode,
                        MaterielName = storage.ProductName,
                        BatchNo = storage.BatchNo,
                        AllocateNo = storage.AllocateNo,
                        AllocateProductId = storage.AllocateProductId,
                        IssueMaterialBillNo = storage.BillNo,
                        BatchNoBig = storage.BatchNoBig,
                        BatchNoSmall = storage.BatchNoSmall,
                        MaterielCompany = storage.ProductCompany,
                        MaterielSpecs = storage.ProductSpecs,
                        Quantity = workOrderBom.SingleCount,
                        CreateBy = model.CreateBy,
                        CreateTime = DateTime.Now
                    };
                    siteMaterials.Add(curSiteBom);

                    try { mesDb.LineStorages.Attach(storage); } catch (Exception ex) { }
                    storage.Quantity = storage.Quantity - curSiteBom.Quantity;
                    storage.ModifyBy = model.CreateBy;
                    storage.ModifyTime = DateTime.Now;

                    if (curSiteBom.MaterielCategoryCode.Equals("0001", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.BatteryCode = curSiteBom.MaterielCode;
                        component.BatterySpecs = curSiteBom.MaterielSpecs;
                        component.BatteryFactory = curSiteBom.MaterielCompany;
                        component.BatteryBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0003", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.RibbonCode = curSiteBom.MaterielCode;
                        component.RibbonSpecs = curSiteBom.MaterielSpecs;
                        component.RibbonCompany = curSiteBom.MaterielCompany;
                        component.RibbonBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0008", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.GlassCode = curSiteBom.MaterielCode;
                        component.GlassSpecs = curSiteBom.MaterielSpecs;
                        component.GlassFactory = curSiteBom.MaterielCompany;
                        component.GlassBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0007", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.BackPlanCode = curSiteBom.MaterielCode;
                        component.BackPlanSpecs = curSiteBom.MaterielSpecs;
                        component.BackPlanFactory = curSiteBom.MaterielCompany;
                        component.BackPlanBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0005", StringComparison.CurrentCultureIgnoreCase)
                        || curSiteBom.MaterielCategoryCode.Equals("0006", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.EvaCode = curSiteBom.MaterielCode;
                        component.EvaSpecs = curSiteBom.MaterielSpecs;
                        component.EvaCompany = curSiteBom.MaterielCompany;
                        component.EvaBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0010", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.ShortFrameCode = curSiteBom.MaterielCode;
                        component.ShortFrameSpecs = curSiteBom.MaterielSpecs;
                        component.ShortFrameCompany = curSiteBom.MaterielCompany;
                        component.ShortFrameBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0009", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.LongFrameCode = curSiteBom.MaterielCode;
                        component.AluFrameSpecs = curSiteBom.MaterielSpecs;
                        component.AluFrameFactory = curSiteBom.MaterielCompany;
                        component.AluFrameBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0014", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.JunBoxCode = curSiteBom.MaterielCode;
                        component.JunBoxSpecs = curSiteBom.MaterielSpecs;
                        component.JunBoxFactory = curSiteBom.MaterielCompany;
                        component.JunBoxBatchNo = curSiteBom.BatchNo;
                    }
                    else if (curSiteBom.MaterielCategoryCode.Equals("0012", StringComparison.CurrentCultureIgnoreCase)
                        || curSiteBom.MaterielCategoryCode.Equals("0013", StringComparison.CurrentCultureIgnoreCase))
                    {
                        component.SilicagelCode = curSiteBom.MaterielCode;
                    }
                }
                mesDb.ProductViaSiteMaterials.AddRange(siteMaterials);
            }
            #endregion

            var entity = Mapper.Map<ProductViaSiteRecord>(model);

            //非层压过站 || 层压首次过站（层压多次过站只记录参数）
            if (model.SiteType != SiteType.Top || !component.TopTime.HasValue)
            {
                mesDb.ProductViaSiteRecords.Add(entity);
            }
            mesDb.SaveChanges();

            if (component.ProduceStatus == ProduceStatus.NotProduced)
            {
                component.ProduceStatus = ProduceStatus.InProduction;
                component.ModifyBy = entity.CreateBy;
                component.ModifyTime = DateTime.Now;
            }
            if (!component.LastSite.HasValue || component.LastSite.Value < model.SiteType)
            {
                component.LastSite = model.SiteType;
            }

            if (component.Reworking)
            {
                var rework = mesDb.ReworkOrders.AsNoTracking().FirstOrDefault(_ => _.ReworkNo == component.ReworkNo && _.ProduceStatus != ProduceStatus.Produced && _.Valid);
                if (rework != null)
                {
                    var reworkSites = mesDb.ReworkSites.AsNoTracking().Where(_ => _.ReworkNo == rework.ReworkNo && _.Valid).Select(_ => _.SiteType).ToList();
                    var existSites = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.Code == component.BarCode && _.ViaSiteType == ViaSiteType.Rework && _.Valid).ToList();
                    if (!reworkSites.Any(r => !existSites.Select(_ => _.SiteType).Contains(r)))
                    {
                        component.Reworking = false;
                    }
                    var allReworkingComponents = mesDb.Components.AsNoTracking().Where(_ => _.ReworkNo == rework.ReworkNo && _.Reworking && _.Valid).ToList();
                    if (!allReworkingComponents.Any(_ => !StringUtils.Equals(_.BarCode, component.BarCode)))
                    {
                        mesDb.ReworkOrders.Attach(rework);
                        rework.ProduceStatus = ProduceStatus.Produced;
                        rework.ModifyBy = model.CreateBy;
                        rework.ModifyTime = DateTime.Now;
                    }
                    else if (rework.ProduceStatus == ProduceStatus.NotProduced)
                    {
                        mesDb.ReworkOrders.Attach(rework);
                        rework.ProduceStatus = ProduceStatus.InProduction;
                        rework.ModifyBy = model.CreateBy;
                        rework.ModifyTime = DateTime.Now;
                    }
                }
                mesDb.SaveChanges();
            }
            if (workOrder.Status == ProduceStatus.NotProduced)
            {
                mesDb.WorkOrders.Attach(workOrder);
                workOrder.Status = ProduceStatus.InProduction;
            }

            if (images != null && images.Any())
            {
                var siteImages = images.Select(_ => new ProductViaSiteImage
                {
                    SiteType = _.SiteType,
                    BarCode = _.BarCode,
                    Image = _.Image,
                    Positions = _.Positions,
                    ViaSiteId = entity.PkId,
                    SortNo = images.IndexOf(_) + 1,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                }).ToList();
                mesDb.ProductViaSiteImages.AddRange(siteImages);
            }

            #region 焊接参数
            if (model.SiteType == SiteType.Welding)
            {
                if (weldingData != null)
                {
                    weldingData.ViaSiteId = entity.PkId;
                    weldingData.CreateTime = DateTime.Now;
                    mesDb.ProductViaSiteWeldDatas.Add(Mapper.Map<ProductViaSiteWeldData>(weldingData));
                }

                if (!component.WeldingTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.LineId = machine.LineId;
                    component.LineName = machine.LineName;

                    component.WeldingTime = DateTime.Now;
                    component.WeldingLineId = machine?.LineId ?? 0;
                    component.WeldingLine = machine?.LineName;
                    component.WeldingDutyId = duty?.PkId ?? 0;
                    component.WeldingDuty = duty?.DutyName;
                    component.WeldingMachineId = machine?.PkId ?? 0;
                    component.WeldingMachine = machine?.MachineName;
                    component.WeldingUser = @operator?.TrueName;
                }
            }
            #endregion

            #region 叠层参数
            if (model.SiteType == SiteType.Laminated && !component.LaminatedTime.HasValue)
            {
                component.LastSiteTime = DateTime.Now;
                component.LaminatedTime = DateTime.Now;
                component.LaminatedLineId = machine?.LineId ?? 0;
                component.LaminatedLine = machine?.LineName;
                component.LaminatedDutyId = duty?.PkId ?? 0;
                component.LaminatedDuty = duty?.DutyName;
                component.LaminatedMachineId = machine?.PkId ?? 0;
                component.LaminatedMachine = machine?.MachineName;
                component.LaminatedUser = @operator?.TrueName;
            }
            #endregion

            #region EL1参数
            if (model.SiteType == SiteType.EL1)
            {
                if (!component.EL1OncePass.HasValue //第一次过站
                    || component.EL1OncePass.Value && !StringUtils.Equals(model.QLevel, "Q1")) //以前合格，现在不合格了，需要修改（以前不合格就永远不“一次合格”）
                {
                    component.EL1OncePass = StringUtils.Equals(model.QLevel, "Q1");
                }
                component.EL1Image = images?.FirstOrDefault()?.Image ?? string.Empty;

                if (!component.EL1Time.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.EL1Time = DateTime.Now;
                    component.EL1LineId = machine?.LineId ?? 0;
                    component.EL1Line = machine?.LineName;
                    component.EL1DutyId = duty?.PkId ?? 0;
                    component.EL1Duty = duty?.DutyName;
                    component.EL1MachineId = machine?.PkId ?? 0;
                    component.EL1Machine = machine?.MachineName;
                    component.EL1User = @operator?.TrueName;
                }
            }
            #endregion

            #region 层压参数
            if (model.SiteType == SiteType.Top && topData != null)
            {
                var topSetting = mesDb.TopMachineSettings.AsNoTracking().Where(_ => _.MachineId == model.MachineId && _.Layer == topData.Layer && _.Cavity == topData.Cavity && _.Valid)
                    .OrderByDescending(_ => _.PkId).FirstOrDefault();
                topData.ViaSiteId = entity.PkId;
                topData.SettingId = topSetting == null ? 0 : topSetting.PkId;
                topData.CreateTime = DateTime.Now;
                mesDb.ProductViaSiteTopDatas.Add(Mapper.Map<ProductViaSiteTopData>(topData));

                if (!component.TopTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.TopTime = DateTime.Now;
                    component.TopLineId = machine?.LineId ?? 0;
                    component.TopLine = machine?.LineName;
                    component.TopDutyId = duty?.PkId ?? 0;
                    component.TopDuty = duty?.DutyName;
                    component.TopMachineId = machine?.PkId ?? 0;
                    component.TopMachine = machine?.MachineName;
                    component.TopUser = @operator?.TrueName;
                    component.TopPosition = model.TopPosition;
                }
            }
            #endregion

            #region NQC
            if (model.SiteType == SiteType.NQC)
            {
                productBadReasons.ForEach(_ => _.JudgeId = entity.PkId);
                mesDb.ProductViaSiteBadReasons.AddRange(productBadReasons);

                if (!component.NQCOncePass.HasValue //第一次过站
                    || component.NQCOncePass.Value && !StringUtils.Equals(model.QLevel, "Q1")) //以前合格，现在不合格了，需要修改（以前不合格就永远不“一次合格”）
                {
                    component.NQCOncePass = StringUtils.Equals(model.QLevel, "Q1");
                }

                if (!component.NQCTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.QLevel = model.QLevel;
                    component.NQCTime = DateTime.Now;
                    component.NQCLineId = machine?.LineId ?? 0;
                    component.NQCLine = machine?.LineName;
                    component.NQCDutyId = duty?.PkId ?? 0;
                    component.NQCDuty = duty?.DutyName;
                    component.NQCMachineId = machine?.PkId ?? 0;
                    component.NQCMachine = machine?.MachineName;
                    component.NQCUser = @operator?.TrueName;
                }
            }
            #endregion

            #region 接线盒拍照
            if (model.SiteType == SiteType.Framing)
            {
                component.BoxImage = images?.FirstOrDefault()?.Image ?? string.Empty;

                if (!component.FramingTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.FramingTime = DateTime.Now;
                    component.FramingLineId = machine?.LineId ?? 0;
                    component.FramingLine = machine?.LineName;
                    component.FramingDutyId = duty?.PkId ?? 0;
                    component.FramingDuty = duty?.DutyName;
                    component.FramingMachineId = machine?.PkId ?? 0;
                    component.FramingMachine = machine?.MachineName;
                    component.FramingUser = @operator?.TrueName;
                }
            }
            #endregion

            #region 清洗
            if (model.SiteType == SiteType.Clean && !component.CleanTime.HasValue)
            {
                component.LastSiteTime = DateTime.Now;

                component.CleanTime = DateTime.Now;
                component.CleanLineId = machine?.LineId ?? 0;
                component.CleanLine = machine?.LineName;
                component.CleanDutyId = duty?.PkId ?? 0;
                component.CleanDuty = duty?.DutyName;
                component.CleanMachineId = machine?.PkId ?? 0;
                component.CleanMachine = machine?.MachineName;
                component.CleanUser = @operator?.TrueName;
            }
            #endregion

            #region IV数据记录
            if (model.SiteType == SiteType.IVTest)
            {
                ivData.ViaSiteId = entity.PkId;
                var ivDataEntity = Mapper.Map<ProductViaSiteIVData>(ivData);
                ivDataEntity.CreateTime = DateTime.Now;
                mesDb.ProductViaSiteIVDatas.Add(ivDataEntity);

                component.IvImage = images?.FirstOrDefault()?.Image ?? string.Empty;
                component.NameplateModel = ivData.NameplateModel;
                component.IVPowerLevel = ivData.IVPowerLevel;
                component.PowerRange = $"{power.PowerMin.ToString("0")}-{power.PowerMax.ToString("0")}";
                component.IVCurrentLevel = ivData.IVCurrentLevel;
                component.ConversionEfficiency = component.TheoreticalPower == 0 ? 0 : ((ivData.Pmax ?? 0) / component.TheoreticalPower);
                component.Isc = ivData.Isc;
                component.Voc = ivData.Voc;
                component.Vm = ivData.Vm;
                component.Im = ivData.Im;
                component.FF = ivData.FF;
                component.Pmax = ivData.Pmax;
                component.Temp = ivData.Temp;
                component.Ress = ivData.Ress;
                component.Ressh = ivData.Ressh;
                component.ModEff = ivData.ModEff;
                component.SunRef = ivData.SunRef;

                if (!component.IVTestTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.IVTestTime = DateTime.Now;
                    component.IVTestLineId = machine?.LineId ?? 0;
                    component.IVTestLine = machine?.LineName;
                    component.IVTestDutyId = duty?.PkId ?? 0;
                    component.IVTestDuty = duty?.DutyName;
                    component.IVTestMachineId = machine?.PkId ?? 0;
                    component.IVTestMachine = machine?.MachineName;
                    component.IVTestUser = @operator?.TrueName;
                }
            }
            #endregion

            #region 绝缘耐压数据记录
            if (model.SiteType == SiteType.InsulatedResistance)
            {
                insulateData.ViaSiteId = entity.PkId;
                insulateData.CreateBy = model.CreateBy;
                insulateData.CreateTime = DateTime.Now;
                var insulateDataEntity = Mapper.Map<ProductViaSiteInsulateData>(insulateData);
                insulateDataEntity.CreateTime = DateTime.Now;
                mesDb.ProductViaSiteInsulateDatas.Add(insulateDataEntity);

                if (!component.InsulatedResistanceTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.InsulatedResistanceTime = DateTime.Now;
                    component.InsulatedResistanceLineId = machine?.LineId ?? 0;
                    component.InsulatedResistanceLine = machine?.LineName;
                    component.InsulatedResistanceDutyId = duty?.PkId ?? 0;
                    component.InsulatedResistanceDuty = duty?.DutyName;
                    component.InsulatedResistanceMachineId = machine?.PkId ?? 0;
                    component.InsulatedResistanceMachine = machine?.MachineName;
                    component.InsulatedResistanceUser = @operator?.TrueName;
                }
            }
            #endregion

            #region EL2拍照
            if (model.SiteType == SiteType.EL2)
            {
                if (!component.EL2OncePass.HasValue //第一次过站
                    || component.EL2OncePass.Value && !StringUtils.Equals(model.QLevel, "Q1")) //以前合格，现在不合格了，需要修改（以前不合格就永远不“一次合格”）
                {
                    component.EL2OncePass = StringUtils.Equals(model.QLevel, "Q1");
                }
                component.EL2Image = images?.FirstOrDefault()?.Image ?? string.Empty;

                if (!component.EL2Time.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.EL2Time = DateTime.Now;
                    component.EL2LineId = machine?.LineId ?? 0;
                    component.EL2Line = machine?.LineName;
                    component.EL2DutyId = duty?.PkId ?? 0;
                    component.EL2Duty = duty?.DutyName;
                    component.EL2MachineId = machine?.PkId ?? 0;
                    component.EL2Machine = machine?.MachineName;
                    component.EL2User = @operator?.TrueName;
                }
            }
            #endregion

            #region 铭牌打印
            if (model.SiteType == SiteType.NameplatePrinting && !component.NameplatePrintingTime.HasValue)
            {
                component.LastSiteTime = DateTime.Now;

                component.NameplatePrintingTime = DateTime.Now;
                component.NameplatePrintingLineId = machine?.LineId ?? 0;
                component.NameplatePrintingLine = machine?.LineName;
                component.NameplatePrintingDutyId = duty?.PkId ?? 0;
                component.NameplatePrintingDuty = duty?.DutyName;
                component.NameplatePrintingMachineId = machine?.PkId ?? 0;
                component.NameplatePrintingMachine = machine?.MachineName;
                component.NameplatePrintingUser = @operator?.TrueName;
            }
            #endregion

            #region 终检
            if (model.SiteType == SiteType.FinalInspection)
            {
                if (!string.IsNullOrWhiteSpace(component.QLevel) && StringUtils.Equals(model.QLevel, "Q1") && !StringUtils.Equals(component.QLevel, "Q1"))
                {
                    throw new MultiLanBizException(110037);//组件已被降级，终检无法升级为Q1
                }
                if (model.IsLock)
                {
                    //组件在终检被锁定
                    new LockMgr().Lock(new List<string> { model.Code }, SiteType.FinalInspection, SiteType.Packing, model.CreateBy, "终检锁定");
                }
                if (!StringUtils.Equals(model.QLevel, "Q1"))
                {
                    //组件在终检被降级
                    new DowngradeMgr().Downgrade(new List<string> { component.BarCode },
                        model.BadCode.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(), SiteType.FinalInspection, model.QLevel, model.CreateBy);
                }
                if (!component.FinalInspectionOncePass.HasValue //第一次过站
                    || component.FinalInspectionOncePass.Value && !StringUtils.Equals(model.QLevel, "Q1")) //以前合格，现在不合格了，需要修改（以前不合格就永远不“一次合格”）
                {
                    component.FinalInspectionOncePass = StringUtils.Equals(model.QLevel, "Q1");
                }

                component.QLevel = entity.QLevel;
                //  component.VerifyCode = model.VerifyCode;

                if (!component.FinalInspectionTime.HasValue)
                {
                    component.LastSiteTime = DateTime.Now;

                    component.FinalInspectionTime = DateTime.Now;
                    component.FinalInspectionLineId = machine?.LineId ?? 0;
                    component.FinalInspectionLine = machine?.LineName;
                    component.FinalInspectionDutyId = duty?.PkId ?? 0;
                    component.FinalInspectionDuty = duty?.DutyName;
                    component.FinalInspectionMachineId = machine?.PkId ?? 0;
                    component.FinalInspectionMachine = machine?.MachineName;
                    component.FinalInspectionUser = @operator?.TrueName;
                    component.FinalInspectionCellFeature = model.CellFeature;
                    component.FinalInspectionComponentType = model.ComponentType;
                    component.FinalInspectionBadReason = model.BadReason;
                    component.VerifyCode = model.VerifyCode;
                }
            }
            #endregion

            #region 打包
            if (model.SiteType == SiteType.Packing && !component.PackTime.HasValue)
            {
                component.LastSiteTime = DateTime.Now;

                component.PackTime = DateTime.Now;
                component.PackLineId = machine?.LineId ?? 0;
                component.PackLine = machine?.LineName;
                component.PackDutyId = duty?.PkId ?? 0;
                component.PackDuty = duty?.DutyName;
                component.PackMachineId = machine?.PkId ?? 0;
                component.PackMachine = machine?.MachineName;
                component.PackUser = @operator?.TrueName;
            }
            #endregion

            mesDb.SaveChanges();

            new TaskFactory().StartNew(() =>
            {
                var log = new VOprLog
                {
                    SubjectType = "组件",
                    SubjectNo = model.Code,
                    LogName = "组件过站",
                    LogStatus = "正常",
                    OpeatorId = model.CreateBy,
                    OpeatorName = @operator?.TrueName,
                    LogContent = $"组件过站：{model.CNSiteType} 过站机台：{model.MachineName} 过站位置:{model.TopPosition} 组件等级:{model.QLevel} 不良原因：{model.BadReason}",
                    LogTime = DateTime.Now,
                    CreateBy = model.CreateBy
                };
                new OprLogMgr().AddLogs(new List<VOprLog> { log });
            });

            model = Mapper.Map<VProductViaSiteRecord>(entity);
            return model;
        }

        /// <summary>
        /// 查询层压参数
        /// </summary>
        public List<VProductViaSiteTopData> QueryTopDatas(string barCode)
        {
            var entities = mesDb.ProductViaSiteTopDatas.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteTopData>>(entities);
        }

        /// <summary>
        /// 查询手动降级记录
        /// </summary>
        public Tuple<int, List<VManuallyDowngradeRecord>> QueryManuallyDowngrades(int page, int limit, string workshopCode, DateTime startTime, DateTime endTime, string orderNo, string workOrderNo, string barCode)
        {
            var query = mesDb.ManuallyDowngradeRecords.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.BarCode == barCode);
            }
            else
            {
                query = query.Where(_ => _.WorkshopCode == workshopCode && _.CreateTime >= startTime && _.CreateTime < endTime);
                if (!string.IsNullOrWhiteSpace(workOrderNo))
                {
                    query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                }
                else if (!string.IsNullOrWhiteSpace(orderNo))
                {
                    query = query.Where(_ => _.OrderNo == orderNo);
                }
            }

            var count = query.Count();
            query = query.OrderByDescending(_ => _.PkId);
            var skip = (page - 1) * limit;
            var entities = query.Skip(skip).Take(limit);
            var models = Mapper.Map<List<VManuallyDowngradeRecord>>(entities);
            var userIds = models.Select(_ => _.CreateBy).Distinct().ToList();
            var users = new UserMgr().QueryUsers(userIds);
            models.ForEach(model =>
            {
                model.CNCreateBy = users.FirstOrDefault(_ => _.PkId == model.CreateBy)?.TrueName;
            });
            return new Tuple<int, List<VManuallyDowngradeRecord>>(count, models);
        }

        /// <summary>
        /// 执行手动降级
        /// </summary>
        /// <param name="barCodes">条码列表</param>
        /// <param name="schemaId">分档规则ID</param>
        /// <param name="userId">执行人</param>
        public void ManuallyDowngrade(List<string> barCodes, int schemaId, int userId)
        {
            barCodes = barCodes.Where(_ => !string.IsNullOrWhiteSpace(_)).Select(_ => _.ToUpper().Trim()).Distinct().ToList();
            if (!barCodes.Any())
            {
                return;
            }
            var user = new UserMgr().QueryUsers(new List<int>(userId)).FirstOrDefault();
            var components = mesDb.Components.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList();
            var ivDatas = mesDb.ProductViaSiteIVDatas.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList()
                .GroupBy(_ => _.BarCode).Select(_ => _.OrderByDescending(c => c.PkId).FirstOrDefault()).ToList();
            var powers = new BinningSchemeMgr().QueryBinningPowers(schemaId);

            var records = new List<ManuallyDowngradeRecord>();
            foreach (var component in components)
            {
                var power = powers.FirstOrDefault(_ => component.Pmax >= _.PowerMin && component.Pmax < _.PowerMax);
                if (power == null)
                {
                    continue;
                }
                var current = power.Currents.FirstOrDefault(_ => component.Im >= _.CurrentMin && component.Im < _.CurrentMax);
                if (current == null)
                {
                    continue;
                }
                var ivData = ivDatas.FirstOrDefault(_ => string.Equals(_.BarCode, component.BarCode, StringComparison.CurrentCultureIgnoreCase));
                if (ivData == null)
                {
                    continue;
                }

                var record = new ManuallyDowngradeRecord
                {
                    WorkshopCode = component.WorkshopCode,
                    WorkshopName = component.WorkshopName,
                    OrderNo = component.OrderNo,
                    WorkOrderNo = component.WorkOrderNo,
                    BarCode = component.BarCode,
                    FF = component.FF,
                    Im = component.Im,
                    Isc = component.Isc,
                    Pmax = component.Pmax,
                    Vm = component.Vm,
                    Voc = component.Voc,
                    OldPower = (int)component.IVPowerLevel,
                    OldNameplateModel = component.NameplateModel,
                    OldCurrent = component.IVCurrentLevel,
                    NewPower = (int)power.Power,
                    NewNameplateModel = power.NameplateModel,
                    NewCurrent = current.LevelName,
                    CreateBy = userId,
                    CreateTime = DateTime.Now
                };
                records.Add(record);

                mesDb.Components.Attach(component);
                component.IVPowerLevel = power.Power;
                component.NameplateModel = power.NameplateModel;
                component.IVCurrentLevel = current.LevelName;

                mesDb.ProductViaSiteIVDatas.Attach(ivData);
                ivData.PowerId = power.PkId;
                ivData.IVPowerLevel = power.Power;
                ivData.NameplateModel = power.NameplateModel;
                ivData.IVCurrentLevel = current.LevelName;
            }
            mesDb.ManuallyDowngradeRecords.AddRange(records);
            mesDb.SaveChanges();

            new OprLogMgr().AddLogs(records.Select(_ => new VOprLog
            {
                SubjectType = "组件",
                SubjectNo = _.BarCode,
                LogName = "手动降级",
                LogStatus = "正常",
                OpeatorId = userId,
                OpeatorName = user?.TrueName,
                LogContent = $"原功率：{_.OldPower}，原铭牌：{_.OldNameplateModel}，新功率：{_.NewPower}，新铭牌：{_.NewNameplateModel}",
                LogTime = DateTime.Now,
                CreateBy = 0
            }).ToList());
        }

        private List<VProductViaSiteRecord> ConvertViaSiteRecords(VComponent component)
        {
            var records = new List<VProductViaSiteRecord>();
            var siteTypes = new CommonMgr().QueryEnumTypes(typeof(SiteType)).Where(_ => _.Key != (int)SiteType.Sort).ToList();
            foreach (var siteType in siteTypes)
            {
                var model = new VProductViaSiteRecord
                {
                    SiteType = (SiteType)siteType.Key,
                    ViaSiteType = ViaSiteType.Produce,
                    Code = component.BarCode,
                    CreateTime = DateTime.MinValue
                };
                switch ((SiteType)siteType.Key)
                {
                    case SiteType.Welding:
                        model.CreateTime = component.WeldingTime ?? DateTime.MinValue;
                        break;
                    case SiteType.Laminated:
                        model.CreateTime = component.LaminatedTime ?? DateTime.MinValue;
                        break;
                    case SiteType.EL1:
                        model.CreateTime = component.EL1Time ?? DateTime.MinValue;
                        break;
                    case SiteType.Top:
                        model.CreateTime = component.TopTime ?? DateTime.MinValue;
                        break;
                    //case SiteType.ELMID:
                    //    model.CreateTime = component.TopTime ?? DateTime.MinValue;
                    //    break;
                    case SiteType.NQC:
                        model.CreateTime = component.NQCTime ?? DateTime.MinValue;
                        break;
                    case SiteType.Framing:
                        model.CreateTime = component.FramingTime ?? DateTime.MinValue;
                        break;
                    case SiteType.Clean:
                        model.CreateTime = component.CleanTime ?? DateTime.MinValue;
                        break;
                    case SiteType.IVTest:
                        model.CreateTime = component.IVTestTime ?? DateTime.MinValue;
                        break;
                    case SiteType.InsulatedResistance:
                        model.CreateTime = component.InsulatedResistanceTime ?? DateTime.MinValue;
                        break;
                    case SiteType.EL2:
                        model.CreateTime = component.EL2Time ?? DateTime.MinValue;
                        break;
                    case SiteType.NameplatePrinting:
                        model.CreateTime = component.NameplatePrintingTime ?? DateTime.MinValue;
                        break;
                    case SiteType.FinalInspection:
                        model.CreateTime = component.FinalInspectionTime ?? DateTime.MinValue;
                        break;
                    case SiteType.Packing:
                        model.CreateTime = !string.IsNullOrWhiteSpace(component.PackNo) ? (component.PackTime ?? DateTime.MinValue) : DateTime.MinValue;
                        break;
                }
                if (model.CreateTime != DateTime.MinValue)
                {
                    records.Add(model);
                }
            }
            return records;
        }

        /// <summary>
        /// 查询漏扫记录
        /// </summary>
        /// <param name="page">页面索引</param>
        /// <param name="limit">页面容量</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="total">全部数量</param>
        /// <param name="siteType">站点类型</param>
        /// <param name="machineId">设备ID</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>漏扫记录</returns>
        public List<VComponent> QueryMissingRecords(int page, int limit, out int total, string workshopCode, SiteType siteType, int machineId, DateTime startTime, DateTime endTime)
        {
            var query = mesDb.Components.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.DistributeTime.HasValue && _.DistributeTime >= startTime && _.DistributeTime < endTime && _.Valid);
            var machine = machineId <= 0 ? null : mesDb.Machines.AsNoTracking().FirstOrDefault(_ => _.PkId == machineId && _.Valid);
            if (machine != null)
            {
                query = query.Where(_ => _.LineId == machine.LineId);
            }
            switch (siteType)
            {
                case SiteType.Welding:
                    query = query.Where(_ => !_.WeldingTime.HasValue);
                    break;
                case SiteType.Laminated:
                    query = query.Where(_ => !_.LaminatedTime.HasValue);
                    break;
                case SiteType.EL1:
                    query = query.Where(_ => !_.EL1Time.HasValue);
                    break;
                case SiteType.Top:
                    query = query.Where(_ => !_.TopTime.HasValue);
                    break;
                case SiteType.NQC:
                    query = query.Where(_ => !_.NQCTime.HasValue);
                    break;
                case SiteType.Framing:
                    query = query.Where(_ => !_.FramingTime.HasValue);
                    break;
                case SiteType.Clean:
                    query = query.Where(_ => !_.CleanTime.HasValue);
                    break;
                case SiteType.IVTest:
                    query = query.Where(_ => !_.IVTestTime.HasValue);
                    break;
                case SiteType.InsulatedResistance:
                    query = query.Where(_ => !_.InsulatedResistanceTime.HasValue);
                    break;
                case SiteType.EL2:
                    query = query.Where(_ => !_.EL2Time.HasValue);
                    break;
                case SiteType.NameplatePrinting:
                    query = query.Where(_ => !_.NameplatePrintingTime.HasValue);
                    break;
                case SiteType.FinalInspection:
                    query = query.Where(_ => !_.FinalInspectionTime.HasValue);
                    break;
                case SiteType.Packing:
                    query = query.Where(_ => !_.PackTime.HasValue);
                    break;
            }
            total = query.Count();
            query = query.OrderBy(_ => _.PkId);
            var entities = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();

            return Mapper.Map<List<VComponent>>(entities);
        }

        /// <summary>
        /// 查询过站IV数据
        /// </summary>
        /// <param name="barCodes">条码列表</param>
        /// <returns>IV数据列表</returns>
        public List<VProductViaSiteIVData> QueryIvDatas(List<string> barCodes)
        {
            var ivDatas = Mapper.Map<List<VProductViaSiteIVData>>(mesDb.ProductViaSiteIVDatas.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList());
            return ivDatas;
        }

        /// <summary>
        /// 查询过站图片列表
        /// </summary>
        /// <param name="barCode">条码</param>
        /// <returns>图片列表</returns>
        public List<VProductViaSiteImage> QueryViaSiteImages(string barCode)
        {
            var entities = mesDb.ProductViaSiteImages.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteImage>>(entities);
        }

        /// <summary>
        /// 查询IV数据
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <returns>IV数据</returns>
        public List<VProductViaSiteIVData> QueryIvDatas(string barCode)
        {
            var entities = mesDb.ProductViaSiteIVDatas.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteIVData>>(entities);
        }

        /// <summary>
        /// 查询绝缘耐压参数
        /// </summary>
        public List<VProductViaSiteInsulateData> QueryInsulateDatas(List<string> barCodes)
        {
            var entities = mesDb.ProductViaSiteInsulateDatas.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteInsulateData>>(entities.GroupBy(_ => _.BarCode).Select(_ => _.LastOrDefault()).ToList());
        }

        /// <summary>
        /// 查询绝缘耐压参数
        /// </summary>
        /// <param name="barCode">组件条码</param>
        public List<VProductViaSiteInsulateData> QueryInsulateDatas(string barCode)
        {
            var entities = mesDb.ProductViaSiteInsulateDatas.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteInsulateData>>(entities);
        }

        /// <summary>
        /// 查询焊机参数
        /// </summary>
        /// <param name="barCode">条码</param>
        /// <returns>参数列表</returns>
        public List<VProductViaSiteWeldData> QueryWeldingDatas(string barCode)
        {
            var entities = mesDb.ProductViaSiteWeldDatas.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).ToList()
                .OrderBy(_ => _.PkId).ToList();
            return Mapper.Map<List<VProductViaSiteWeldData>>(entities);
        }

        /// <summary>
        /// 过去过站图片
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <param name="siteType"></param>
        /// <returns>图片列表</returns>
        public List<string> GetImages(string barCode, SiteType siteType)
        {
            var images = mesDb.ProductViaSiteImages.Where(_ => _.SiteType == siteType && _.BarCode == barCode && _.Valid).OrderByDescending(_ => _.PkId).ToList();
            return images.Select(_ => _.Image).ToList();
        }

        /// <summary>
        /// 获取过站IV数据
        /// </summary>
        /// <param name="barCode"></param>
        /// <returns></returns>
        public VProductViaSiteIVData GetIvData(string barCode)
        {
            var ivData = mesDb.ProductViaSiteIVDatas.AsNoTracking().Where(_ => _.BarCode == barCode && _.Valid).OrderByDescending(_ => _.PkId).FirstOrDefault();
            return Mapper.Map<VProductViaSiteIVData>(ivData);
        }

        /// <summary>
        /// 获取过站IV数据
        /// </summary>
        /// <param name="barCode"></param>
        /// <returns></returns>
        public VProductViaSiteIVData GetImage(string barCode)
        {
            var ivData = mesDb.ProductViaSiteIVDatas.AsNoTracking().FirstOrDefault(_ => _.BarCode == barCode && _.Valid);
            return Mapper.Map<VProductViaSiteIVData>(ivData);
        }

        /// <summary>
        /// 获取过站记录
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <param name="siteType">站点类型</param>
        /// <returns>过站记录</returns>
        public List<VProductViaSiteRecord> GetProductViaSiteRecord(string barCode, SiteType? siteType)
        {
            var query = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.Code == barCode && _.Valid);
            if (siteType.HasValue)
            {
                query = query.Where(_ => _.SiteType == siteType.Value);
            }
            var entities = query.ToList();
            return Mapper.Map<List<VProductViaSiteRecord>>(entities);
        }
        /// <summary>
        /// 获取过站记录
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <param name="siteType">站点类型</param>
        /// <returns>过站记录</returns>
        public VProductViaSiteRecord GetProductViaSiteRecord(string barCode, string qLevel, SiteType siteType)
        {

            var query = mesDb.ProductViaSiteRecords.AsNoTracking().FirstOrDefault(_ => _.Code == barCode && _.SiteType == siteType && _.QLevel != qLevel && _.Valid);

            var entities = query;

            return Mapper.Map<VProductViaSiteRecord>(entities);

        }

        /// <summary>
        /// 获取过站记录
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <param name="siteType">站点类型</param>
        /// <returns>过站记录</returns>
        public List<VProductViaSiteRecord> QueryProductViaSiteRecords(List<string> barCodes, SiteType? siteType)
        {
            var query = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => barCodes.Contains(_.Code) && _.Valid);
            if (siteType.HasValue)
            {
                query = query.Where(_ => _.SiteType == siteType.Value);
            }
            var entities = query.ToList();
            return Mapper.Map<List<VProductViaSiteRecord>>(entities);
        }

        /// <summary>
        /// 查询组件过站记录
        /// </summary>
        /// <param name="pageIndex">页面索引</param>
        /// <param name="pageSize">页面容量</param>
        /// <param name="total">全部数量</param>
        /// <param name="dutyId">班次ID</param>
        /// <param name="siteType">过站类型</param>
        /// <returns>过站列表</returns>
        public List<VProductViaSiteRecord> QueryViaSiteRecords(int pageIndex, int pageSize, out int total, int dutyId, SiteType siteType, int machineId = 0)
        {
            total = 0;
            var duty = mesDb.WorkshopDuties.AsNoTracking().FirstOrDefault(_ => _.PkId == dutyId && _.Valid);
            if (duty == null || !duty.StartTime.HasValue || !duty.EndTime.HasValue)
            {
                return new List<VProductViaSiteRecord>();
            }

            var startTime = DateTime.Today.Add(duty.StartTime.Value);
            var endTime = duty.StartTime < duty.EndTime ? DateTime.Today.Add(duty.EndTime.Value) : DateTime.Today.AddDays(1).Add(duty.EndTime.Value);
            if (DateTime.Now < startTime)
            {
                startTime = startTime.AddDays(-1);
                endTime = endTime.AddDays(-1);
            }

            IQueryable<ProductViaSiteRecord> query = null;
            if (machineId > 0)
            {
                query = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.MachineId == machineId && _.Valid && _.CreateTime >= startTime && _.CreateTime < endTime);
            }
            else
            {
                query = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.DutyId == dutyId && _.SiteType == siteType && _.Valid && _.CreateTime >= startTime && _.CreateTime < endTime);
            }

            total = query.Select(_ => _.Code).Distinct().Count();
            query = query.OrderByDescending(_ => _.PkId);
            var entities = pageSize <= 0 ? query.ToList() : query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            return Mapper.Map<List<VProductViaSiteRecord>>(entities);
        }

        /// <summary>
        /// 查询车间过站记录
        /// </summary>
        /// <param name="page">页面索引</param>
        /// <param name="limit">页面容量</param>
        /// <param name="total">全部数量</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="siteType">站点类型</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="workOrderNo">工单号</param>
        /// <param name="barCode">组件条码</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns></returns>
        public List<VProductViaSiteRecord> QueryViaSiteRecords(int page, int limit, out int total, List<string> workshopCodes, int lineId, SiteType? siteType, string orderNo, string workOrderNo, string barCode, DateTime startTime, DateTime endTime)
        {
            if (!workshopCodes.Any())
            {
                total = 0;
                return new List<VProductViaSiteRecord>();
            }

            var query = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.Code == barCode);
            }
            else
            {
                query = query.Where(_ => workshopCodes.Contains(_.WorkshopCode) && _.CreateTime >= startTime && _.CreateTime < endTime);
                if (lineId > 0)
                {
                    query = query.Where(_ => _.LineId == lineId);
                }
                if (siteType.HasValue)
                {
                    query = query.Where(_ => (int)_.SiteType == (int)siteType.Value);
                }
                if (!string.IsNullOrWhiteSpace(workOrderNo))
                {
                    query = query.Where(_ => _.WorkOrderNo == workOrderNo);
                }
                else if (!string.IsNullOrWhiteSpace(orderNo))
                {
                    query = query.Where(_ => _.OrderNo == orderNo);
                }
            }
            total = query.Count();

            query = query.OrderByDescending(_ => _.PkId);
            var entities = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();

            return Mapper.Map<List<VProductViaSiteRecord>>(entities);
        }

        /// <summary>
        /// 获取当班产量
        /// </summary>
        /// <param name="dutyId">班次ID</param>
        /// <param name="siteType">设备类型</param>
        /// <param name="machineId">设备ID</param>
        /// <returns>产量</returns>
        public int GetCurDutyViaSiteOutput(int dutyId, SiteType siteType, int machineId = 0)
        {
            var duty = mesDb.WorkshopDuties.AsNoTracking().FirstOrDefault(_ => _.PkId == dutyId && _.Valid);
            if (duty == null || !duty.StartTime.HasValue || !duty.EndTime.HasValue)
            {
                return 0;
            }

            var startTime = DateTime.Today.Add(duty.StartTime.Value);
            var endTime = duty.StartTime < duty.EndTime ? DateTime.Today.Add(duty.EndTime.Value) : DateTime.Today.AddDays(1).Add(duty.EndTime.Value);
            if (DateTime.Now < startTime)
            {
                startTime = startTime.AddDays(-1);
                endTime = endTime.AddDays(-1);
            }


            var count = 0;
            switch (siteType)
            {
                case SiteType.Welding:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.WeldingMachineId == machineId && _.WeldingTime >= startTime && _.WeldingTime < endTime && _.Valid);
                    break;
                case SiteType.Laminated:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.LaminatedMachineId == machineId && _.LaminatedTime >= startTime && _.LaminatedTime < endTime && _.Valid);
                    break;
                case SiteType.LaminatorRework:
                    var query = mesDb.ProductViaSiteRecords.Where(_ => _.SiteType == SiteType.LaminatorRework && _.CreateTime >= startTime && _.CreateTime < endTime && _.Valid).Select(_ => _.Code).Distinct().ToList();
                    count = query.Count();
                    break;
                case SiteType.EL1:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.EL1MachineId == machineId && _.EL1Time >= startTime && _.EL1Time < endTime && _.Valid);
                    break;
                case SiteType.Top:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.WorkshopCode == duty.WorkshopCode && _.TopTime >= startTime && _.TopTime < endTime && _.Valid);
                    break;
                case SiteType.NQC:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.NQCMachineId == machineId && _.NQCTime >= startTime && _.NQCTime < endTime && _.Valid);
                    break;
                case SiteType.Framing:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.FramingMachineId == machineId && _.FramingTime >= startTime && _.FramingTime < endTime && _.Valid);
                    break;
                case SiteType.Clean:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.CleanMachineId == machineId && _.CleanTime >= startTime && _.CleanTime < endTime && _.Valid);
                    break;
                case SiteType.IVTest:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.IVTestMachineId == machineId && _.IVTestTime >= startTime && _.IVTestTime < endTime && _.Valid);
                    break;
                case SiteType.InsulatedResistance:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.InsulatedResistanceMachineId == machineId && _.InsulatedResistanceTime >= startTime && _.InsulatedResistanceTime < endTime && _.Valid);
                    break;
                case SiteType.EL2:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.EL2MachineId == machineId && _.EL2Time >= startTime && _.EL2Time < endTime && _.Valid);
                    break;
                case SiteType.NameplatePrinting:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.NameplatePrintingMachineId == machineId && _.NameplatePrintingTime >= startTime && _.NameplatePrintingTime < endTime && _.Valid);
                    break;
                case SiteType.FinalInspection:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.FinalInspectionMachineId == machineId && _.FinalInspectionTime >= startTime && _.FinalInspectionTime < endTime && _.Valid);
                    break;
                case SiteType.Packing:
                    count = mesDb.Components.AsNoTracking().Count(_ => _.PackMachineId == machineId && _.PackTime >= startTime && _.PackTime < endTime && _.Valid);
                    break;
            }

            return count;
        }

        /// <summary>
        /// 终检过站
        /// </summary>
        /// <param name="qLevel"></param>
        /// <param name="barCodes"></param>
        public void ViaFinalSite(string qLevel, List<string> barCodes)
        {
            var components = mesDb.Components.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList();
            if (!components.Any())
            {
                return;
            }

            var existsRecords = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => barCodes.Contains(_.Code) && _.SiteType == SiteType.FinalInspection && _.Valid).ToList();

            var records = new List<ProductViaSiteRecord>();
            foreach (var component in components)
            {
                mesDb.Components.Attach(component);
                component.QLevel = !string.IsNullOrWhiteSpace(component.QLevel) ? component.QLevel : qLevel;
                component.FinalInspectionCellFeature = !string.IsNullOrWhiteSpace(component.FinalInspectionCellFeature) ? component.FinalInspectionCellFeature : "圆倒角";
                component.FinalInspectionComponentType = !string.IsNullOrWhiteSpace(component.FinalInspectionComponentType) ? component.FinalInspectionComponentType : "m12zj-B";
                component.FinalInspectionDutyId = component.FinalInspectionDutyId > 0 ? component.FinalInspectionDutyId : 1;
                component.FinalInspectionDuty = !string.IsNullOrWhiteSpace(component.FinalInspectionDuty) ? component.FinalInspectionDuty : "A班";
                component.FinalInspectionLineId = component.FinalInspectionLineId > 0 ? component.FinalInspectionLineId : 1;
                component.FinalInspectionLine = !string.IsNullOrWhiteSpace(component.FinalInspectionLine) ? component.FinalInspectionLine : "A线";
                component.FinalInspectionMachineId = component.FinalInspectionMachineId > 0 ? component.FinalInspectionMachineId : 22;
                component.FinalInspectionMachine = !string.IsNullOrWhiteSpace(component.FinalInspectionMachine) ? component.FinalInspectionMachine : "A线终检机台";
                component.FinalInspectionOncePass = component.FinalInspectionOncePass.HasValue ? component.FinalInspectionOncePass : true;
                component.FinalInspectionUser = !string.IsNullOrWhiteSpace(component.FinalInspectionUser) ? component.FinalInspectionUser : "m12zj-A";
                component.FinalInspectionTime = component.FinalInspectionTime.HasValue ? component.FinalInspectionTime : (component.DistributeTime ?? component.CreateTime).AddHours(10);
                component.ModifyBy = -1;
                component.ModifyTime = DateTime.Now;

                if (!existsRecords.Any(_ => StringUtils.Equals(_.Code, component.BarCode)))
                {
                    records.Add(new ProductViaSiteRecord
                    {
                        Code = component.BarCode,
                        MachineId = component.FinalInspectionMachineId,
                        MachineName = component.FinalInspectionMachine,
                        SiteType = SiteType.FinalInspection,
                        CellFeature = component.FinalInspectionCellFeature,
                        ComponentType = component.FinalInspectionComponentType,
                        DutyId = component.FinalInspectionDutyId,
                        DutyName = component.FinalInspectionDuty,
                        LineId = component.FinalInspectionLineId,
                        LineName = component.FinalInspectionLine,
                        ViaSiteType = ViaSiteType.Produce,
                        WorkOrderNo = component.WorkOrderNo,
                        OrderNo = component.OrderNo,
                        WorkshopCode = component.WorkshopCode,
                        WorkshopName = component.WorkshopName,
                        QLevel = component.QLevel,
                        Valid = true,
                        CreateBy = -1,
                        CreateTime = component.FinalInspectionTime.Value
                    });
                }
            }
            mesDb.ProductViaSiteRecords.AddRange(records);

            mesDb.SaveChanges();
        }

        /// <summary>
        /// 过站数据补偿
        /// </summary>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        public void DataCompensation(DateTime startTime, DateTime endTime)
        {
            var records = mesDb.ProductViaSiteRecords.AsNoTracking().Where(_ => _.CreateTime >= startTime && _.CreateTime < endTime && _.Valid).ToList();
            var barCodes = records.Select(_ => _.Code).Select(_ => _.ToUpper().Trim()).Distinct().ToList();
            var components = mesDb.Components.AsNoTracking().Where(_ => barCodes.Contains(_.BarCode) && _.Valid).ToList();
            var userIds = records.Select(_ => _.CreateBy).Distinct().ToList();
            var users = mesDb.Users.AsNoTracking().Where(_ => userIds.Contains(_.PkId)).ToList();

            foreach (var items in records.GroupBy(_ => _.Code))
            {
                var component = components.FirstOrDefault(_ => string.Equals(items.Key, _.BarCode, StringComparison.CurrentCultureIgnoreCase));
                if (component == null)
                {
                    logger.Fatal($"过站组件不存在，组件条码：{items.Key}");
                    continue;
                }

                mesDb.Components.Attach(component);
                foreach (var record in items.GroupBy(_ => _.SiteType))
                {
                    switch (record.Key)
                    {
                        case SiteType.Welding:
                            if (!component.WeldingTime.HasValue)
                            {
                                component.WeldingLineId = record.FirstOrDefault().LineId;
                                component.WeldingLine = record.FirstOrDefault().LineName;
                                component.WeldingMachineId = record.FirstOrDefault().MachineId;
                                component.WeldingMachine = record.FirstOrDefault().MachineName;
                                component.WeldingDutyId = record.FirstOrDefault().DutyId;
                                component.WeldingDuty = record.FirstOrDefault().DutyName;
                                component.WeldingTime = record.FirstOrDefault().CreateTime;
                                component.WeldingUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.Laminated:
                            if (!component.LaminatedTime.HasValue)
                            {
                                component.LaminatedLineId = record.FirstOrDefault().LineId;
                                component.LaminatedLine = record.FirstOrDefault().LineName;
                                component.LaminatedMachineId = record.FirstOrDefault().MachineId;
                                component.LaminatedMachine = record.FirstOrDefault().MachineName;
                                component.LaminatedDutyId = record.FirstOrDefault().DutyId;
                                component.LaminatedDuty = record.FirstOrDefault().DutyName;
                                component.LaminatedTime = record.FirstOrDefault().CreateTime;
                                component.LaminatedUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.EL1:
                            if (!component.EL1Time.HasValue)
                            {
                                component.EL1LineId = record.FirstOrDefault().LineId;
                                component.EL1Line = record.FirstOrDefault().LineName;
                                component.EL1MachineId = record.FirstOrDefault().MachineId;
                                component.EL1Machine = record.FirstOrDefault().MachineName;
                                component.EL1DutyId = record.FirstOrDefault().DutyId;
                                component.EL1Duty = record.FirstOrDefault().DutyName;
                                component.EL1Time = record.FirstOrDefault().CreateTime;
                                component.EL1User = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.Top:
                            if (!component.TopTime.HasValue)
                            {
                                component.TopLineId = record.FirstOrDefault().LineId;
                                component.TopLine = record.FirstOrDefault().LineName;
                                component.TopMachineId = record.FirstOrDefault().MachineId;
                                component.TopMachine = record.FirstOrDefault().MachineName;
                                component.TopDutyId = record.FirstOrDefault().DutyId;
                                component.TopDuty = record.FirstOrDefault().DutyName;
                                component.TopTime = record.FirstOrDefault().CreateTime;
                                component.TopUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.NQC:
                            if (!component.NQCTime.HasValue)
                            {
                                component.NQCLineId = record.FirstOrDefault().LineId;
                                component.NQCLine = record.FirstOrDefault().LineName;
                                component.NQCMachineId = record.FirstOrDefault().MachineId;
                                component.NQCMachine = record.FirstOrDefault().MachineName;
                                component.NQCDutyId = record.FirstOrDefault().DutyId;
                                component.NQCDuty = record.FirstOrDefault().DutyName;
                                component.NQCTime = record.FirstOrDefault().CreateTime;
                                component.NQCUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.Framing:
                            if (!component.FramingTime.HasValue)
                            {
                                component.FramingLineId = record.FirstOrDefault().LineId;
                                component.FramingLine = record.FirstOrDefault().LineName;
                                component.FramingMachineId = record.FirstOrDefault().MachineId;
                                component.FramingMachine = record.FirstOrDefault().MachineName;
                                component.FramingDutyId = record.FirstOrDefault().DutyId;
                                component.FramingDuty = record.FirstOrDefault().DutyName;
                                component.FramingTime = record.FirstOrDefault().CreateTime;
                                component.FramingUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.Clean:
                            if (!component.CleanTime.HasValue)
                            {
                                component.CleanLineId = record.FirstOrDefault().LineId;
                                component.CleanLine = record.FirstOrDefault().LineName;
                                component.CleanMachineId = record.FirstOrDefault().MachineId;
                                component.CleanMachine = record.FirstOrDefault().MachineName;
                                component.CleanDutyId = record.FirstOrDefault().DutyId;
                                component.CleanDuty = record.FirstOrDefault().DutyName;
                                component.CleanTime = record.FirstOrDefault().CreateTime;
                                component.CleanUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.IVTest:
                            if (!component.IVTestTime.HasValue)
                            {
                                component.IVTestLineId = record.FirstOrDefault().LineId;
                                component.IVTestLine = record.FirstOrDefault().LineName;
                                component.IVTestMachineId = record.FirstOrDefault().MachineId;
                                component.IVTestMachine = record.FirstOrDefault().MachineName;
                                component.IVTestDutyId = record.FirstOrDefault().DutyId;
                                component.IVTestDuty = record.FirstOrDefault().DutyName;
                                component.IVTestTime = record.FirstOrDefault().CreateTime;
                                component.IVTestUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.InsulatedResistance:
                            if (!component.InsulatedResistanceTime.HasValue)
                            {
                                component.InsulatedResistanceLineId = record.FirstOrDefault().LineId;
                                component.InsulatedResistanceLine = record.FirstOrDefault().LineName;
                                component.InsulatedResistanceMachineId = record.FirstOrDefault().MachineId;
                                component.InsulatedResistanceMachine = record.FirstOrDefault().MachineName;
                                component.InsulatedResistanceDutyId = record.FirstOrDefault().DutyId;
                                component.InsulatedResistanceDuty = record.FirstOrDefault().DutyName;
                                component.InsulatedResistanceTime = record.FirstOrDefault().CreateTime;
                                component.InsulatedResistanceUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.EL2:
                            if (!component.EL2Time.HasValue)
                            {
                                component.EL2LineId = record.FirstOrDefault().LineId;
                                component.EL2Line = record.FirstOrDefault().LineName;
                                component.EL2MachineId = record.FirstOrDefault().MachineId;
                                component.EL2Machine = record.FirstOrDefault().MachineName;
                                component.EL2DutyId = record.FirstOrDefault().DutyId;
                                component.EL2Duty = record.FirstOrDefault().DutyName;
                                component.EL2Time = record.FirstOrDefault().CreateTime;
                                component.EL2User = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.NameplatePrinting:
                            if (!component.NameplatePrintingTime.HasValue)
                            {
                                component.NameplatePrintingLineId = record.FirstOrDefault().LineId;
                                component.NameplatePrintingLine = record.FirstOrDefault().LineName;
                                component.NameplatePrintingMachineId = record.FirstOrDefault().MachineId;
                                component.NameplatePrintingMachine = record.FirstOrDefault().MachineName;
                                component.NameplatePrintingDutyId = record.FirstOrDefault().DutyId;
                                component.NameplatePrintingDuty = record.FirstOrDefault().DutyName;
                                component.NameplatePrintingTime = record.FirstOrDefault().CreateTime;
                                component.NameplatePrintingUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.FinalInspection:
                            if (!component.FinalInspectionTime.HasValue)
                            {
                                component.FinalInspectionLineId = record.FirstOrDefault().LineId;
                                component.FinalInspectionLine = record.FirstOrDefault().LineName;
                                component.FinalInspectionMachineId = record.FirstOrDefault().MachineId;
                                component.FinalInspectionMachine = record.FirstOrDefault().MachineName;
                                component.FinalInspectionDutyId = record.FirstOrDefault().DutyId;
                                component.FinalInspectionDuty = record.FirstOrDefault().DutyName;
                                component.FinalInspectionTime = record.FirstOrDefault().CreateTime;
                                component.FinalInspectionUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                        case SiteType.Packing:
                            if (!component.PackTime.HasValue)
                            {
                                component.PackLineId = record.FirstOrDefault().LineId;
                                component.PackLine = record.FirstOrDefault().LineName;
                                component.PackMachineId = record.FirstOrDefault().MachineId;
                                component.PackMachine = record.FirstOrDefault().MachineName;
                                component.PackDutyId = record.FirstOrDefault().DutyId;
                                component.PackDuty = record.FirstOrDefault().DutyName;
                                component.PackTime = record.FirstOrDefault().CreateTime;
                                component.PackUser = users.FirstOrDefault(_ => _.PkId == record.FirstOrDefault().CreateBy)?.TrueName;
                            }
                            break;
                    }
                }
            }
            mesDb.SaveChanges();
        }
    }
}
